package com.example.debuglib;

import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.content.Intent;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbPalette;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Nebneb on 26/10/2016 at 11:27.
 */

public class DevToolFragment extends Fraction {

    private static final int MINIFY_WIDTH = 132;
    private int CONSOLE_HEIGHT = 110;
    private int CONSOLE_WIDTH = 250;
    private int CONSOLE_TEXT_SIZE = 12;

    private ComponentContainer mRootView;
    private boolean isAdded = false;

    private LayoutScatter mInflater;
    private List<DebugFunction> mFunctions = new ArrayList<>();

    private Text mConsole;
    private ScrollView mConsoleContainer;

    private Component mPanel;

    private Component mMinifyButton;


    private DirectionalLayout directionalLayout;

    private float dX;
    private float dY;

    private float startX = 0;
    private float startY = 0;

    private float startx, endx;
    private float starty, endy;

    private DevToolTheme mTheme = DevToolTheme.DARK;
    private Context context;

    public DevToolFragment(Context context) {
        this.context = context;
    }

    void displayAt(float x, float y) {
        this.startX = x;
        this.startY = y;
    }

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
    }

    @Override
    public Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
        mInflater = scatter;
        mRootView = (ComponentContainer) scatter.parse(ResourceTable.Layout_debugkit_fragment_dev_tools, container, false);
        isAdded = true;
        return mRootView;
    }

    @Override
    public void onComponentDetach() {
        isAdded = false;
    }

    @Override
    protected void onActive() {
        super.onActive();
        DirectionalLayout mButtonContainer = (DirectionalLayout) mRootView.findComponentById(ResourceTable.Id_debugkit_button_container);

        for (int i = 0; i < mFunctions.size(); i++) {

            Button button = (Button) mInflater
                    .parse(mTheme == DevToolTheme.DARK ? ResourceTable.Layout_debugkit_function_button_dark : ResourceTable.Layout_debugkit_function_button_light, mButtonContainer, false);
            final DebugFunction function = mFunctions.get(i);
            final String title = function.title == null ? "F" + (i + 1) : function.title;

            if (function.title != null) {
                ComponentContainer.LayoutConfig layoutconfig = button.getLayoutConfig();
                layoutconfig.width = DirectionalLayout.LayoutConfig.MATCH_CONTENT;
                button.setLayoutConfig(layoutconfig);
            }

            button.setText(title);
            button.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    try {
                        if (function != null) {
                            String result = function.call();
                            if (result != null)
                                log(title + ": " + result);
                        } else
                            log(title + " is undefined");
                    } catch (Exception e) {
                        log("Error: see logcat for more details");
                        e.printStackTrace();
                    }
                }
            });

            mButtonContainer.addComponent(button);
        }
        directionalLayout = (DirectionalLayout) mRootView.findComponentById(ResourceTable.Id_textcontent);
        directionalLayout.setLayoutRefreshedListener(new Component.LayoutRefreshedListener() {
            @Override
            public void onRefreshed(Component component) {
                mConsoleContainer.fluentScrollYTo(directionalLayout.getHeight());
            }
        });
        mConsole = new Text(context);
        mConsoleContainer = (ScrollView) mRootView.findComponentById(ResourceTable.Id_debugkit_console_scroll_view);

        mMinifyButton = mRootView.findComponentById(ResourceTable.Id_debugkit_tools_minify);

        mPanel = mRootView.findComponentById(ResourceTable.Id_debugkit_tools_panel);

        mRootView.findComponentById(ResourceTable.Id_debugkit_tools_close_button).setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                if (isAdded) {
                    try {
                        getFractionAbility().getFractionManager().startFractionScheduler()
                                .remove(DevToolFragment.this).submit();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        mRootView.setTouchEventListener(
                new Component.TouchEventListener() {
                    @Override
                    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
                        return DevToolFragment.this.onTouch(component, touchEvent);
                    }
                });

        ComponentContainer.LayoutConfig layoutConfig = mConsoleContainer.getLayoutConfig();
        layoutConfig.height = dpTopX(CONSOLE_HEIGHT);
        mConsoleContainer.setLayoutConfig(layoutConfig);

        layoutConfig = directionalLayout.getLayoutConfig();
        layoutConfig.width = dpTopX(CONSOLE_WIDTH);
        directionalLayout.setLayoutConfig(layoutConfig);
        directionalLayout.setMinHeight(dpTopX(CONSOLE_HEIGHT));

        mPanel.setPosition((int) startX, (int) startY);

        mMinifyButton.setTag(false);
        mMinifyButton.setClickedListener(new Component.ClickedListener() {

            @Override
            public void onClick(Component component) {
                switchMinify();
            }
        });
        applyTheme();

        softLog("ready.");
    }

    /**
     * Switch the tool to minify mode.
     */
    private void switchMinify() {

        AnimatorProperty rotateAnimation;
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                float x = startx + (endx - startx) * v;
                float y = starty + (endy - starty) * v;
                ComponentContainer.LayoutConfig layoutConfig = mConsoleContainer.getLayoutConfig();
                layoutConfig.width = (int) x;
                layoutConfig.height = (int) y;

                mConsoleContainer.setLayoutConfig(layoutConfig);


            }
        });
        if ((boolean) mMinifyButton.getTag()) {
            rotateAnimation = new AnimatorProperty();
            rotateAnimation.rotate(0);
            startx = dpTopX(MINIFY_WIDTH);
            endx = dpTopX(CONSOLE_WIDTH);
            starty = 0;
            endy = dpTopX(CONSOLE_HEIGHT);
            mMinifyButton.setTag(false);
        } else {
            rotateAnimation = new AnimatorProperty();
            rotateAnimation.rotate(180);
            startx = dpTopX(CONSOLE_WIDTH);
            endx = dpTopX(MINIFY_WIDTH);
            starty = dpTopX(CONSOLE_HEIGHT);
            endy = 0;
            mMinifyButton.setTag(true);
        }

        animatorValue.setDuration(200);
        animatorValue.start();
        rotateAnimation.setDuration(200);
        rotateAnimation.setTarget(mMinifyButton);
        rotateAnimation.start();

    }

    private boolean onTouch(Component v, TouchEvent event) {

        switch (event.getAction()) {

            case TouchEvent.PRIMARY_POINT_DOWN:
                Component componentMove = v.findComponentById(ResourceTable.Id_move);
                int y = componentMove.getHeight() + dpTopX(6);
                if (event.getPointerPosition(event.getIndex()).getY() > y)
                    return false;
                dX = event.getPointerPosition(event.getIndex()).getX();
                dY = event.getPointerScreenPosition(event.getIndex()).getY() - v.getContentPositionY();
                break;
            case TouchEvent.POINT_MOVE:
                v.setContentPositionX(event.getPointerScreenPosition(event.getIndex()).getX() - dX);
                v.setContentPositionY(event.getPointerScreenPosition(event.getIndex()).getY() - dY);
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                break;
        }

        return true;
    }

    /**
     * Call this function at runtime if you want to log something in the console.
     *
     * @param string the message that will be logged to to the console.
     *               <p>
     *               string will be logged in the console on a new line as following:
     *               <br>
     *               {@code HH:mm:ss > string}
     */
    public void log(final String string) {
        final StringBuilder sb = new StringBuilder();
        sb.append(getCurrentTime()).append(" > ");
        sb.append(string);
        write(sb.toString());
    }

    /**
     * Call this function at runtime if you want to clear the console.
     */
    public void clear() {
        directionalLayout.removeAllComponents();
        softLog("ready.");
    }

    private void softLog(String string) {
        final StringBuilder sb = new StringBuilder();
        sb.append(getCurrentTime()).append(" > ");
        sb.append(string);
        write(sb.toString());
    }

    private void write(final String string) {
        Text text = new Text(context);
        text.setTextSize(mConsole.getTextSize());
        text.setTextColor(mConsole.getTextColor());
        text.setText(string);
        text.setMultipleLine(true);
        text.setPadding(dpTopX(4), dpTopX(4), dpTopX(4), dpTopX(4));
        directionalLayout.addComponent(text);
        getFractionAbility().getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                directionalLayout.postLayout();
                if (mConsoleContainer != null) {
                    mConsoleContainer.fluentScrollYTo(directionalLayout.getHeight());
                    mConsoleContainer.postLayout();
                }
            }
        });
    }

    /**
     * Add a function to the list. This will add a button as well when calling {@code build()}
     *
     * @param function must implement {@link DebugFunction}.
     */
    public void addFunction(DebugFunction function) {
        this.mFunctions.add(function);
    }

    /**
     * Set the function list. This will corresponding buttons when calling {@code build()}
     *
     * @param functions must be a List of {@link DebugFunction}.
     */
    public void setFunctionList(List<DebugFunction> functions) {
        this.mFunctions = functions;
    }

    /**
     * Set the console text size. Must be called after having called build()
     *
     * @param sp the size of the text in sp.
     */
    public void changeConsoleTextSize(final int sp) {

        context.getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                mConsole.setTextSize(sp2px(sp));
            }
        });
    }

    /**
     * Set the console text size. The size will be applied on build.
     *
     * @param sp the size of the text in sp.
     */
    public void setConsoleTextSize(final int sp) {
        CONSOLE_TEXT_SIZE = sp;
    }

    /**
     * Set the theme of the debug tool
     *
     * @param theme can be {@code DevToolTheme.LIGHT} or {@code DevToolTheme.DARK}.
     *              The default theme is {@code DevToolTheme.DARK}
     */
    public void setTheme(DevToolTheme theme) {
        this.mTheme = theme;
    }


    /**
     * This method will be called on build. You can call this method if you want to change the
     * theme of the console theme at runtime.
     */
    public void applyTheme() {
        switch (mTheme) {
            case LIGHT:
                ShapeElement elementW = new ShapeElement(context, ResourceTable.Graphic_debug_kit_background_light);
                mConsoleContainer.setBackground(elementW);
                directionalLayout.setBackground(elementW);
                mConsole.setTextColor(new Color(RgbPalette.parse("#000")));
                break;
            default:
                ShapeElement elementB = new ShapeElement(context, ResourceTable.Graphic_debug_kit_background_black);
                mConsoleContainer.setBackground(elementB);
                directionalLayout.setBackground(elementB);
                mConsole.setTextColor(new Color(RgbPalette.parse("#8FB5C3")));

        }

        mConsole.setTextSize(sp2px(CONSOLE_TEXT_SIZE));
    }

    private int dpTopX(int dp) {
        DisplayManager displayManager = DisplayManager.getInstance();
        Optional<Display> optional = displayManager.getDefaultDisplay(context);
        final float scale = optional.get().getAttributes().densityPixels;
        return (int) (dp * scale + 0.5f);
    }

    private int sp2px(float spValue) {
        final float scale = DisplayManager.getInstance().getDefaultDisplay(context).get().getAttributes().densityPixels;
        return (int) (spValue * scale + 0.5f);
    }

    private String getCurrentTime() {
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
        return df.format(Calendar.getInstance().getTime());
    }

    /**
     * Set the console height.
     *
     * @param consoleHeight represents the console height in dp.
     */
    public void setConsoleHeight(int consoleHeight) {
        this.CONSOLE_HEIGHT = consoleHeight;
    }

    /**
     * Set the console width.
     *
     * @param consoleWidth represents the console width in dp.
     */
    public void setConsoleWidth(int consoleWidth) {
        this.CONSOLE_WIDTH = consoleWidth;
    }


    /**
     * Enum, theme choices for the debug tool.
     */
    public enum DevToolTheme {
        DARK,
        LIGHT
    }
}
